package api

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
	"io"
	cache "monitor_serve/cache/redis"
	"monitor_serve/meta"
	"monitor_serve/service"
	"monitor_serve/util"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

type MultipartFileApi struct {
}

// 初始化信息
type MultipartUploadInfo struct {
	FileHash   string
	FileSize   int64
	UploadID   string
	ChunkSize  int
	ChunkCount int
}

// 信息初始化
func (m *MultipartFileApi) InitiateMultipartUploadHandler(c *gin.Context) {
	// 1. 解析请求参数
	fileHash := c.PostForm("fileHash")
	fileName := c.PostForm("fileName")
	userName := c.PostForm("userName")
	fileSizeStr := c.PostForm("fileSize")
	fileSize, err := strconv.ParseInt(fileSizeStr, 10, 64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid file size"})
		return
	}
	if fileHash == "" || fileName == "" || userName == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing required parameters"})
		return
	}

	// 1.2 是否触发秒传
	fileMeta, err := service.ServiceGroupApp.FileService.GetFileMeta(fileHash)
	if fileMeta != nil {
		// 触发秒传
		err = service.ServiceGroupApp.FileService.UserFileFinished(userName, fileHash, fileName, fileSize)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "写入失败，秒传失败！"})
			return
		}
		c.JSON(http.StatusOK, gin.H{"msg": "File fast upload quickly"})
		return
	}

	// 2. 生成唯一上传ID
	uploadID := userName + fmt.Sprintf("%x", time.Now().UnixNano())
	chunkSize := int64(5 * 1024 * 1024) // 每个分块大小为5MB
	chunkCount := fileSize / chunkSize
	if fileSize%chunkSize > 0 {
		chunkCount++
	}

	// 3. 使用 MSET 存储分块初始化信息
	keys := []string{
		fmt.Sprintf("upload:%s:fileHash", uploadID),
		fmt.Sprintf("upload:%s:fileSize", uploadID),
		fmt.Sprintf("upload:%s:chunkSize", uploadID),
		fmt.Sprintf("upload:%s:chunkCount", uploadID),
	}
	values := []interface{}{
		fileHash,
		fileSize,
		chunkSize,
		chunkCount,
	}

	// 将 values 转换为 map[string]interface{}
	data := make(map[string]interface{})
	for i, key := range keys {
		data[key] = values[i]
	}

	// 为每个分块设置初始状态
	for i := int64(0); i < chunkCount; i++ {
		chunkKey := fmt.Sprintf("upload:%s:chunk:%d", uploadID, i)
		data[chunkKey] = "not_uploaded"
	}

	// 存储上传信息
	uploadInfoKey := fmt.Sprintf("upload:%s:info", uploadID)
	data[uploadInfoKey] = "initialized" // 或者存储其他相关信息
	result, err := cache.RedisClient.MSet(c, data).Result()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to cache upload info"})
		return
	}
	if result == "" {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to cache upload info"})
		return
	}

	// 设置过期时间
	expireDuration := 48 * time.Hour
	for key := range data {
		if err = cache.RedisClient.Expire(c, key, expireDuration).Err(); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to set expiration for key"})
			return
		}
	}

	// 4. 返回上传ID
	c.JSON(http.StatusOK, gin.H{"uploadID": uploadID})
}

// 分块上传
func (m *MultipartFileApi) UploadMultipartHandler(c *gin.Context) {
	// 解析用户请求参数
	uploadID := c.PostForm("uploadID")
	chunkIndexStr := c.PostForm("chunkIndex")
	userName := c.PostForm("userName")

	// 检查参数是否完整
	if uploadID == "" || chunkIndexStr == "" || userName == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing required parameters"})
		return
	}

	// 将 chunkIndex 转换为整数
	chunkIndex, err := strconv.Atoi(chunkIndexStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid chunk index"})
		return
	}

	// 确保临时目录存在
	tempDir := "Temp"
	if _, err := os.Stat(tempDir); os.IsNotExist(err) {
		err = os.MkdirAll(tempDir, 0755)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create temp directory"})
			return
		}
	}

	// 构建分块文件路径
	filePath := filepath.Join(tempDir, fmt.Sprintf("%s_%s_%d", userName, uploadID, chunkIndex))

	// 从请求中获取文件
	fileHeader, err := c.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to get file from request"})
		return
	}

	// 打开文件
	src, err := fileHeader.Open()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open file"})
		return
	}
	defer src.Close()

	// 创建目标文件
	dest, err := os.Create(filePath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create destination file"})
		return
	}
	defer dest.Close()

	// 将文件数据复制到目标文件
	_, err = io.Copy(dest, src)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to copy file data"})
		return
	}

	// 更新 Redis 缓存状态
	chunkKey := fmt.Sprintf("upload:%s:chunk:%d", uploadID, chunkIndex)
	expiration := 48 * time.Hour
	err = cache.RedisClient.Set(c, chunkKey, filePath, expiration).Err()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update Redis cache status"})
		return
	}

	// 打印调试信息
	chunkPath, err := cache.RedisClient.Get(c, chunkKey).Result()
	fmt.Println("chunkKey:", chunkKey, "___chunkPath:", chunkPath)

	// 返回处理结果到客户端
	c.JSON(http.StatusOK, gin.H{"message": "Chunk uploaded successfully"})
}

// 通知上传合并接口
func (m *MultipartFileApi) CompleteUploadHandler(c *gin.Context) {
	// 1. 解析请求参数
	fileHash := c.PostForm("fileHash")
	fileName := c.PostForm("fileName")
	userName := c.PostForm("userName")
	fileSizeStr := c.PostForm("fileSize")
	uploadId := c.PostForm("uploadId")
	fileSize, err := strconv.ParseInt(fileSizeStr, 10, 64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid file size"})
		return
	}
	if fileHash == "" || fileName == "" || userName == "" || uploadId == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing required parameters"})
		return
	}

	// 2. 从 Redis 获取上传信息
	fileHashKey := fmt.Sprintf("upload:%s:fileHash", uploadId)
	fileSizeKey := fmt.Sprintf("upload:%s:fileSize", uploadId)
	chunkSizeKey := fmt.Sprintf("upload:%s:chunkSize", uploadId)
	chunkCountKey := fmt.Sprintf("upload:%s:chunkCount", uploadId)

	fileHashVal, err := cache.RedisClient.Get(c, fileHashKey).Result()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get fileHash from Redis"})
		return
	}

	fileSizeVal, err := cache.RedisClient.Get(c, fileSizeKey).Result()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get fileSize from Redis"})
		return
	}

	chunkSizeVal, err := cache.RedisClient.Get(c, chunkSizeKey).Result()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get chunkSize from Redis"})
		return
	}

	chunkCountVal, err := cache.RedisClient.Get(c, chunkCountKey).Result()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get chunkCount from Redis"})
		return
	}

	// 转换获取到的值
	fileSize, err = strconv.ParseInt(fileSizeVal, 10, 64)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse fileSize"})
		return
	}

	chunkSize, err := strconv.Atoi(chunkSizeVal)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse chunkSize"})
		return
	}

	chunkCount, err := strconv.Atoi(chunkCountVal)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse chunkCount"})
		return
	}

	// 创建 MultipartUploadInfo 结构体
	upInfo := MultipartUploadInfo{
		FileHash:   fileHashVal,
		FileSize:   fileSize,
		UploadID:   uploadId,
		ChunkSize:  chunkSize,
		ChunkCount: chunkCount,
	}
	fmt.Println("upInfo:", upInfo)

	// 3. 通过 uploadId 查询 Redis 并判断是否所有分块上传完成
	for i := 0; i < upInfo.ChunkCount; i++ {
		chunkKey := fmt.Sprintf("upload:%s:chunk:%d", uploadId, i)
		status, err := cache.RedisClient.Get(c, chunkKey).Result()
		fmt.Println("chunkKey:", chunkKey, "___status:", status)
		if err != nil && err != redis.Nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check chunk status in Redis"})
			return
		}
		if status == "not_uploaded" {
			c.JSON(http.StatusConflict, gin.H{"error": "Not all chunks have been uploaded"})
			return
		}
	}

	// 3.2. 获取所有的分块的文件位置
	var chunkPaths []string
	for i := 0; i < upInfo.ChunkCount; i++ {
		chunkKey := fmt.Sprintf("upload:%s:chunk:%d", uploadId, i)
		chunkPath, err := cache.RedisClient.Get(c, chunkKey).Result()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get chunk path from Redis"})
			return
		}
		chunkPaths = append(chunkPaths, chunkPath)
	}

	// 4. 合并
	tempDir := "Temp2"
	finalFilePath := filepath.Join(tempDir, fmt.Sprintf("%s_%s", uploadId, fileName))
	finalFile, err := os.Create(finalFilePath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create final file"})
		return
	}
	defer finalFile.Close()

	for _, chunkPath := range chunkPaths {
		chunkFile, err := os.Open(chunkPath)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open chunk file"})
			return
		}
		defer chunkFile.Close()
		_, err = io.Copy(finalFile, chunkFile)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to copy chunk data"})
			return
		}
	}

	// 4.2 验证完整性
	finalFile.Seek(0, 0)
	mergedHash1 := util.FileSha12(finalFile)
	if mergedHash1 != upInfo.FileHash {
		c.JSON(http.StatusBadRequest, gin.H{"error": "File integrity check failed"})
		return
	}
	fmt.Println("upInfo.FileHash:", upInfo.FileHash)
	fmt.Println("mergedHash1:", mergedHash1)

	// 5. 更新唯一文件表以及用户文件表
	err = service.ServiceGroupApp.FileService.UserFileFinished(userName, fileHash, fileName, fileSize)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err})
		return
	}

	fileMeta := meta.FileMeta{
		FileName: fileName,
		Location: finalFilePath,
		FileSize: fileSize,
		UploadAt: time.Now().Format("2006-01-02 15:04:05"),
		FileSha1: mergedHash1,
	}
	meta.UpdateFileMetaDB(fileMeta)

	// 6. 响应处理结果
	c.JSON(http.StatusOK, gin.H{"msg": "上传成功"})
}

// 新接口：接收用户名和上传的文件，计算文件的哈希值和文件大小，然后调用 InitiateMultipartUploadHandler
func (m *MultipartFileApi) StartMultipartUploadHandler(c *gin.Context) {
	// 1. 解析请求参数
	userName := c.PostForm("userName")
	if userName == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing required parameter: userName"})
		return
	}

	// 2. 从请求中读取文件
	file, err := c.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to read file"})
		return
	}

	// 3. 打开文件
	src, err := file.Open()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open file"})
		return
	}
	defer src.Close()

	// 4. 计算文件的SHA1值
	fileHash := util.FileSha12(src)

	// 5. 重新打开文件以计算文件大小
	src.Seek(0, 0)
	fileSize, err := io.Copy(io.Discard, src)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to compute file size"})
		return
	}

	// 6. 调用 InitiateMultipartUploadHandler 接口
	formData := url.Values{
		"fileHash": {fileHash},
		"fileName": {file.Filename},
		"userName": {userName},
		"fileSize": {fmt.Sprintf("%d", fileSize)},
	}
	resp, err := http.PostForm("http://localhost:8083/api2/mpUploadInit", formData)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to initiate multipart upload"})
		return
	}
	defer resp.Body.Close()

	// 7. 读取响应
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read response"})
		return
	}

	// 8. 返回响应
	c.Data(resp.StatusCode, "application/json", body)
}
